{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "google",
   "metadata": {},
   "source": [
    "##### Copyright 2023 Google LLC."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "apache",
   "metadata": {},
   "source": [
    "Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "you may not use this file except in compliance with the License.\n",
    "You may obtain a copy of the License at\n",
    "\n",
    "    http://www.apache.org/licenses/LICENSE-2.0\n",
    "\n",
    "Unless required by applicable law or agreed to in writing, software\n",
    "distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "See the License for the specific language governing permissions and\n",
    "limitations under the License.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "basename",
   "metadata": {},
   "source": [
    "# transit_time"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "link",
   "metadata": {},
   "source": [
    "<table align=\"left\">\n",
    "<td>\n",
    "<a href=\"https://colab.research.google.com/github/google/or-tools/blob/main/examples/notebook/examples/transit_time.ipynb\"><img src=\"https://raw.githubusercontent.com/google/or-tools/main/tools/colab_32px.png\"/>Run in Google Colab</a>\n",
    "</td>\n",
    "<td>\n",
    "<a href=\"https://github.com/google/or-tools/blob/main/examples/python/transit_time.py\"><img src=\"https://raw.githubusercontent.com/google/or-tools/main/tools/github_32px.png\"/>View source on GitHub</a>\n",
    "</td>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "doc",
   "metadata": {},
   "source": [
    "First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "install",
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install ortools"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "description",
   "metadata": {},
   "source": [
    "Display Transit Time\n",
    "   Distances are in meters and time in minutes.\n",
    "\n",
    "   Manhattan average block: 750ft x 264ft -> 228m x 80m\n",
    "   src: https://nyti.ms/2GDoRIe \"NY Times: Know Your distance\"\n",
    "   here we use: 114m x 80m city block\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "code",
   "metadata": {},
   "outputs": [],
   "source": [
    "from ortools.constraint_solver import pywrapcp\n",
    "from ortools.constraint_solver import routing_enums_pb2\n",
    "\n",
    "\n",
    "###########################\n",
    "# Problem Data Definition #\n",
    "###########################\n",
    "class Vehicle():\n",
    "    \"\"\"Stores the property of a vehicle\"\"\"\n",
    "\n",
    "    def __init__(self):\n",
    "        \"\"\"Initializes the vehicle properties\"\"\"\n",
    "        self._capacity = 15\n",
    "        # Travel speed: 5km/h to convert in m/min\n",
    "        self._speed = 5 * 60 / 3.6\n",
    "\n",
    "    @property\n",
    "    def speed(self):\n",
    "        \"\"\"Gets the average travel speed of a vehicle\"\"\"\n",
    "        return self._speed\n",
    "\n",
    "\n",
    "class CityBlock():\n",
    "    \"\"\"City block definition\"\"\"\n",
    "\n",
    "    @property\n",
    "    def width(self):\n",
    "        \"\"\"Gets Block size West to East\"\"\"\n",
    "        return 228 / 2\n",
    "\n",
    "    @property\n",
    "    def height(self):\n",
    "        \"\"\"Gets Block size North to South\"\"\"\n",
    "        return 80\n",
    "\n",
    "\n",
    "class DataProblem():\n",
    "    \"\"\"Stores the data for the problem\"\"\"\n",
    "\n",
    "    def __init__(self):\n",
    "        \"\"\"Initializes the data for the problem\"\"\"\n",
    "        self._vehicle = Vehicle()\n",
    "\n",
    "        # Locations in block unit\n",
    "        locations = \\\n",
    "                [(4, 4), # depot\n",
    "                 (2, 0), (8, 0), # row 0\n",
    "                 (0, 1), (1, 1),\n",
    "                 (5, 2), (7, 2),\n",
    "                 (3, 3), (6, 3),\n",
    "                 (5, 5), (8, 5),\n",
    "                 (1, 6), (2, 6),\n",
    "                 (3, 7), (6, 7),\n",
    "                 (0, 8), (7, 8)]\n",
    "        # locations in meters using the city block dimension\n",
    "        city_block = CityBlock()\n",
    "        self._locations = [(loc[0] * city_block.width,\n",
    "                            loc[1] * city_block.height) for loc in locations]\n",
    "\n",
    "        self._depot = 0\n",
    "\n",
    "        self._demands = \\\n",
    "            [0, # depot\n",
    "             1, 1, # 1, 2\n",
    "             2, 4, # 3, 4\n",
    "             2, 4, # 5, 6\n",
    "             8, 8, # 7, 8\n",
    "             1, 2, # 9,10\n",
    "             1, 2, # 11,12\n",
    "             4, 4, # 13, 14\n",
    "             8, 8] # 15, 16\n",
    "\n",
    "        self._time_windows = \\\n",
    "            [(0, 0),\n",
    "             (75, 85), (75, 85), # 1, 2\n",
    "             (60, 70), (45, 55), # 3, 4\n",
    "             (0, 8), (50, 60), # 5, 6\n",
    "             (0, 10), (10, 20), # 7, 8\n",
    "             (0, 10), (75, 85), # 9, 10\n",
    "             (85, 95), (5, 15), # 11, 12\n",
    "             (15, 25), (10, 20), # 13, 14\n",
    "             (45, 55), (30, 40)] # 15, 16\n",
    "\n",
    "    @property\n",
    "    def vehicle(self):\n",
    "        \"\"\"Gets a vehicle\"\"\"\n",
    "        return self._vehicle\n",
    "\n",
    "    @property\n",
    "    def locations(self):\n",
    "        \"\"\"Gets locations\"\"\"\n",
    "        return self._locations\n",
    "\n",
    "    @property\n",
    "    def num_locations(self):\n",
    "        \"\"\"Gets number of locations\"\"\"\n",
    "        return len(self.locations)\n",
    "\n",
    "    @property\n",
    "    def depot(self):\n",
    "        \"\"\"Gets depot location index\"\"\"\n",
    "        return self._depot\n",
    "\n",
    "    @property\n",
    "    def demands(self):\n",
    "        \"\"\"Gets demands at each location\"\"\"\n",
    "        return self._demands\n",
    "\n",
    "    @property\n",
    "    def time_per_demand_unit(self):\n",
    "        \"\"\"Gets the time (in min) to load a demand\"\"\"\n",
    "        return 5  # 5 minutes/unit\n",
    "\n",
    "    @property\n",
    "    def time_windows(self):\n",
    "        \"\"\"Gets (start time, end time) for each locations\"\"\"\n",
    "        return self._time_windows\n",
    "\n",
    "\n",
    "#######################\n",
    "# Problem Constraints #\n",
    "#######################\n",
    "def manhattan_distance(position_1, position_2):\n",
    "    \"\"\"Computes the Manhattan distance between two points\"\"\"\n",
    "    return (\n",
    "        abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1]))\n",
    "\n",
    "\n",
    "class CreateTimeEvaluator(object):\n",
    "    \"\"\"Creates callback to get total times between locations.\"\"\"\n",
    "\n",
    "    @staticmethod\n",
    "    def service_time(data, node):\n",
    "        \"\"\"Gets the service time for the specified location.\"\"\"\n",
    "        return data.demands[node] * data.time_per_demand_unit\n",
    "\n",
    "    @staticmethod\n",
    "    def travel_time(data, from_node, to_node):\n",
    "        \"\"\"Gets the travel times between two locations.\"\"\"\n",
    "        if from_node == to_node:\n",
    "            travel_time = 0\n",
    "        else:\n",
    "            travel_time = manhattan_distance(data.locations[\n",
    "                from_node], data.locations[to_node]) / data.vehicle.speed\n",
    "        return travel_time\n",
    "\n",
    "    def __init__(self, data):\n",
    "        \"\"\"Initializes the total time matrix.\"\"\"\n",
    "        self._total_time = {}\n",
    "        # precompute total time to have time callback in O(1)\n",
    "        for from_node in range(data.num_locations):\n",
    "            self._total_time[from_node] = {}\n",
    "            for to_node in range(data.num_locations):\n",
    "                if from_node == to_node:\n",
    "                    self._total_time[from_node][to_node] = 0\n",
    "                else:\n",
    "                    self._total_time[from_node][to_node] = int(\n",
    "                        self.service_time(data, from_node) + self.travel_time(\n",
    "                            data, from_node, to_node))\n",
    "\n",
    "    def time_evaluator(self, from_node, to_node):\n",
    "        \"\"\"Returns the total time between the two nodes\"\"\"\n",
    "        return self._total_time[from_node][to_node]\n",
    "\n",
    "\n",
    "def print_transit_time(route, time_evaluator):\n",
    "    \"\"\"Print transit time between nodes of a route\"\"\"\n",
    "    total_time = 0\n",
    "    for i, j in route:\n",
    "        total_time += time_evaluator(i, j)\n",
    "        print('{0} -> {1}: {2}min'.format(i, j, time_evaluator(i, j)))\n",
    "    print('Total time: {0}min\\n'.format(total_time))\n",
    "\n",
    "\n",
    "########\n",
    "# Main #\n",
    "########\n",
    "def main():\n",
    "    \"\"\"Entry point of the program\"\"\"\n",
    "    # Instantiate the data problem.\n",
    "    data = DataProblem()\n",
    "\n",
    "    # Print Transit Time\n",
    "    time_evaluator = CreateTimeEvaluator(data).time_evaluator\n",
    "    print('Route 0:')\n",
    "    print_transit_time([[0, 5], [5, 8], [8, 6], [6, 2], [2, 0]], time_evaluator)\n",
    "\n",
    "    print('Route 1:')\n",
    "    print_transit_time([[0, 9], [9, 14], [14, 16], [16, 10], [10, 0]],\n",
    "                       time_evaluator)\n",
    "\n",
    "    print('Route 2:')\n",
    "    print_transit_time([[0, 12], [12, 13], [13, 15], [15, 11], [11, 0]],\n",
    "                       time_evaluator)\n",
    "\n",
    "    print('Route 3:')\n",
    "    print_transit_time([[0, 7], [7, 4], [4, 3], [3, 1], [1, 0]], time_evaluator)\n",
    "\n",
    "\n",
    "main()\n",
    "\n"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 5
}
